#include "llvm/Support/CommandLine.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TargetOptions.h"
#include "llvm/Support/Host.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/Frontend/ASTConsumers.h"
#include "llvm/Support/raw_ostream.h"
#include "clang/Parse/ParseAST.h"
#include "clang/AST/ASTContext.h"

static llvm::cl::opt<std::string> FileName(llvm::cl::Positional, llvm::cl::desc("Input file"), llvm::cl::Required);

int file_size(char* filename)  
{
	FILE *fp=fopen(filename,"r");

	if(!fp) {
		return -1;
	}

	fseek(fp, 0, SEEK_END);
	int size=ftell(fp);
	fclose(fp);

	return size;
}

int main(int argc, char **argv)
{
	llvm::cl::ParseCommandLineOptions(argc, argv, "My simple front end\n");

	clang::CompilerInstance CI;
	clang::DiagnosticOptions diagnosticOptions;
	CI.createDiagnostics();

	std::shared_ptr<clang::TargetOptions> PTO = std::make_shared<clang::TargetOptions>();
	PTO->Triple = llvm::sys::getDefaultTargetTriple();
	clang::TargetInfo *PTI = clang::TargetInfo::CreateTargetInfo(CI.getDiagnostics(), PTO);
	CI.setTarget(PTI);

	CI.createFileManager();
	CI.createSourceManager(CI.getFileManager());
	CI.createPreprocessor(clang::TU_Complete);
	CI.getPreprocessorOpts().UsePredefines = false;

	std::unique_ptr< clang::ASTConsumer > astConsumer = clang::CreateASTPrinter(NULL, "");
	CI.setASTConsumer(std::move(astConsumer));

	CI.createASTContext();
	CI.createSema(clang::TU_Complete, NULL);

	const clang::FileEntry *file = CI.getFileManager().getVirtualFile(FileName, file_size(argv[1]), 0);
	if (!file) {
		llvm::errs() << "File not found: " << file;
		return 1;
	}
	clang::FileID mainFileID = CI.getSourceManager().createFileID(file, clang::SourceLocation(), clang::SrcMgr::C_User);
	CI.getSourceManager().setMainFileID(mainFileID);

	CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), 0);
	clang::ParseAST(CI.getSema());

	// Print AST statistics
	CI.getASTContext().PrintStats();
	CI.getASTContext().Idents.PrintStats();

	return 0;
}
